package br.com.ctm.advogadope.rest.impl;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;

import javax.persistence.PersistenceException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;

import br.com.ctm.advogadope.rest.api.EntityResource;
import br.com.framework.negocio.api.BaseNegocio;
import br.com.framework.negocio.exception.NegocioException;
import br.com.framework.persistencia.api.BaseDao;
import br.com.framework.persistencia.model.BaseEntity;

/**
 * @author Cleber
 *
 * @param <E>
 * @param <Negocio>
 */
public abstract class EntityResourceImpl<E extends BaseEntity, Negocio extends BaseNegocio<E, ? extends BaseDao<E>>>
		implements EntityResource<E> {
	
	protected static final Logger LOGGER = Logger.getLogger(EntityResourceImpl.class);

	@Context
	protected UriInfo uriInfo;

	private Negocio negocio;

	/**
	 * @return the negocio
	 */
	protected Negocio getNegocio() {
		return negocio;
	}

	/**
	 * @param negocio the negocio to set
	 */
	protected void setNegocio(Negocio negocio) {
		this.negocio = negocio;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.ctm.advogadope.rest.api.EntityResource#findByID(java.lang.Long)
	 */
	@Override
	public E findByID(Long id) throws PersistenceException {
		return getNegocio().findByID(id);
	}

	/* (non-Javadoc)
	 * @see br.com.ctm.advogadope.rest.api.EntityResource#findAll()
	 */
	@Override
	public List<E> findAll() throws PersistenceException {
		return getNegocio().findAll();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.ctm.advogadope.rest.api.EntityResource#findAll(int, int)
	 */
	@Override
	public List<E> findAll(int firstResult, int maxResults)
			throws PersistenceException {
		return getNegocio().findAll(firstResult, maxResults);
	}

	/* (non-Javadoc)
	 * @see br.com.ctm.advogadope.rest.api.EntityResource#findByExample(br.com.framework.persistencia.model.BaseEntity)
	 */
	@Override
	public List<E> findByExample(E entidade)
			throws PersistenceException {
		return getNegocio().findByExample(entidade);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.ctm.advogadope.rest.api.EntityResource#getRecordCount()
	 */
	@Override
	public Long getRecordCount() throws PersistenceException {
		return getNegocio().getRecordCount();
	}
	
	/**
	 * @param text
	 * @return
	 */
	private String getEncodedText(String text) {
		try {
			return URLEncoder.encode(text, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return text;
	}

	/* (non-Javadoc)
	 * @see br.com.ctm.advogadope.rest.api.EntityResource#insert(br.com.framework.persistencia.model.BaseEntity)
	 */
	@Override
	public Response insert(E entidade) throws NegocioException,
			PersistenceException {
		ResponseBuilder builder = null;
		Response response = null;
		try {
			getNegocio().insert(entidade);
			builder = Response.created(uriInfo.getAbsolutePath());
		} catch (NegocioException e) {
			builder = Response.status(Status.PRECONDITION_FAILED);
			builder.header("errorMessages", 
					getEncodedText(e.getMessage()));
		} catch (Exception e) {
			LOGGER.error("Erro ao inserir entidade.", e);
			Throwable rootCause = ExceptionUtils.getRootCause(e);
			if (rootCause instanceof ConstraintViolationException) {
				builder = Response.status(Status.PRECONDITION_FAILED);
				Iterator<ConstraintViolation<?>> constraintIt = 
						((ConstraintViolationException)rootCause).getConstraintViolations().iterator();
				while (constraintIt.hasNext()) {
					ConstraintViolation<?> violation = constraintIt.next();
					builder.header("errorMessages", 
						 getEncodedText(violation.getMessage()));
				}
			} else {
				builder = Response.status(Status.INTERNAL_SERVER_ERROR);
			}
		}
		response = builder.build();
		return response;
	}


	/* (non-Javadoc)
	 * @see br.com.ctm.advogadope.rest.api.EntityResource#update(java.lang.Long, br.com.framework.persistencia.model.BaseEntity)
	 */
	@Override
	public Response update(Long id, E entidade)
			throws NegocioException, PersistenceException {
		ResponseBuilder builder = null;
		Response response = null;
		try {
			E pEntidade = getNegocio().findByID(id);
			if (pEntidade != null && pEntidade.equals(entidade)) {
				getNegocio().update(entidade);
				builder = Response.created(uriInfo.getAbsolutePath());
			} else {
				builder = Response.noContent();
			}
		} catch (NegocioException e) {
			builder = Response.status(Status.PRECONDITION_FAILED);
			builder.header("errorMessages", 
					getEncodedText(e.getMessage()));
		} catch (Exception e) {
			LOGGER.error("Erro ao editar entidade.", e);
			Throwable rootCause = ExceptionUtils.getRootCause(e);
			if (rootCause instanceof ConstraintViolationException) {
				builder = Response.status(Status.PRECONDITION_FAILED);
				Iterator<ConstraintViolation<?>> constraintIt = 
						((ConstraintViolationException)rootCause).getConstraintViolations().iterator();
				while (constraintIt.hasNext()) {
					ConstraintViolation<?> violation = constraintIt.next();
					builder.header("errorMessages", 
						 getEncodedText(violation.getMessage()));
				}
			} else {
				builder = Response.status(Status.INTERNAL_SERVER_ERROR);
			}
		}
		response = builder.build();
		return response;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see br.com.ctm.advogadope.rest.api.EntityResource#remove(java.lang.Long)
	 */
	@Override
	public Response remove(Long id) throws NegocioException,
			PersistenceException {
		ResponseBuilder builder = null;
		Response response = null;
		try {
			E pEntidade = getNegocio().findByID(id);
			if (pEntidade != null) {
				getNegocio().remove(pEntidade);
				builder = Response.ok();
			} else {
				builder = Response.noContent();
			}
		} catch (NegocioException e) {
			builder = Response.status(Status.PRECONDITION_FAILED);
			builder.header("errorMessages", 
					getEncodedText(e.getMessage()));
		} catch (Exception e) {
			LOGGER.error("Erro ao remover entidade.", e);
			Throwable rootCause = ExceptionUtils.getRootCause(e);
			if (rootCause instanceof ConstraintViolationException) {
				builder = Response.status(Status.PRECONDITION_FAILED);
				Iterator<ConstraintViolation<?>> constraintIt = 
						((ConstraintViolationException)rootCause).getConstraintViolations().iterator();
				while (constraintIt.hasNext()) {
					ConstraintViolation<?> violation = constraintIt.next();
					builder.header("errorMessages", 
						 getEncodedText(violation.getMessage()));
				}
			} else {
				builder = Response.status(Status.INTERNAL_SERVER_ERROR);
			}
		}
		response = builder.build();
		return response;
	}
	

}
